package com.xuzimian.global.demo.spring.security.oauth2.client.config.oauth;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

/**
 * 该类配置oauth2 client客户端，用于自己的oauth2 server登录授权。
 */
@Configuration
@Order(Integer.MIN_VALUE)
public class OAuth2ClientForMyOauthServerConfigurer {

    private static final Log logger = LogFactory.getLog(OAuth2ClientForMyOauthServerConfigurer.class);

    @Resource(name = "mylogin")
    private OAuth2ProtectedResourceDetails details;

    @Bean(name = "mylogin")
    public OAuth2ProtectedResourceDetails remote() {
        AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
        resourceDetails.setAccessTokenUri("http://localhost/oauth/token");
        resourceDetails.setUserAuthorizationUri("http://localhost/oauth/authorize");
        resourceDetails.setClientId("ssoclient");
        resourceDetails.setClientSecret("ssosecret");
        resourceDetails.setScope(Arrays.asList("all"));

        return resourceDetails;
    }

    /**
     * 获取token
     *
     * @param details
     * @return
     */
    @Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2Context, @Qualifier("mylogin") OAuth2ProtectedResourceDetails details) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(details, oauth2Context);

        AuthorizationCodeAccessTokenProvider authCodeProvider = new AuthorizationCodeAccessTokenProvider();
        authCodeProvider.setStateMandatory(false);
        AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(authCodeProvider));
        template.setAccessTokenProvider(provider);

        return template;
    }


    /**
     * 注册处理redirect uri的filter
     * 拦截redirectUri,根据authentication code获取token，依赖前面两个对象)
     *
     * @param oauth2RestTemplate
     * @param tokenService
     * @return
     */
    @Bean(name = "myloginFilter")
    public OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter(OAuth2RestTemplate oauth2RestTemplate, ResourceServerTokenServices tokenService) {
        OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter("/login");
        filter.setRestTemplate(oauth2RestTemplate);
        filter.setTokenServices(tokenService);

        //设置回调成功的页面
        filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                // this.setDefaultTargetUrl("/deny");
                super.onAuthenticationSuccess(request, response, authentication);
            }
        });
        return filter;
    }


    @Bean
    public DefaultTokenServices jwtTokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setTokenStore(jwtTokenStore());
        return services;
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
    }

    @Bean
    public JwtAccessTokenConverter jwtTokenEnhancer() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        String keyValue;
        try {
            keyValue = getKeyFromServer();
        } catch (ResourceAccessException ex) {
            logger.warn("Failed to fetch token key (you may need to refresh "
                    + "when the auth server is back)");
            throw new RuntimeException("请先启动本地的spring-security-oauth2-server程序服务，并保证端口是80.否则将不能获取到证书。");
        }

        if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) {
            converter.setSigningKey(keyValue);
        }
        if (keyValue != null) {
            converter.setVerifierKey(keyValue);
        }
//        AnnotationAwareOrderComparator.sort(this.configurers);
//        for (JwtAccessTokenConverterConfigurer configurer : this.configurers) {
//            configurer.configure(converter);
//        }
        return converter;
    }


    private String getKeyFromServer() {
        HttpHeaders headers = new HttpHeaders();
        String username = details.getClientId();
        String password = details.getClientSecret();
        if (username != null && password != null) {
            byte[] token = Base64.encode((username + ":" + password).getBytes());
            headers.add("Authorization", "Basic " + new String(token));
        }
        HttpEntity<Void> request = new HttpEntity<Void>(headers);
        String url = "http://localhost/oauth/token_key";
        return (String) new RestTemplate()
                .exchange(url, HttpMethod.GET, request, Map.class).getBody()
                .get("value");
    }

    /**
     * 注册check token服务
     *
     * @param details
     * @return
     */
    //@Bean
    public RemoteTokenServices tokenService(OAuth2ProtectedResourceDetails details) {
        RemoteTokenServices tokenService = new RemoteTokenServices();
        tokenService.setCheckTokenEndpointUrl(details.getAccessTokenUri());
        tokenService.setClientId(details.getClientId());
        tokenService.setClientSecret(details.getClientSecret());
        return tokenService;
    }
}
